package main.api;

import com.mongodb.BasicDBObject;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Filters;
import main.*;
import main.cipher.AESUtils;
import main.cipher.RSAUtils;
import main.cipher.TokenUtils;
import main.database.AccountHelper;
import main.database.DatabaseMan;
import main.database.RandomNameHelper;
import org.bson.Document;
import org.json.JSONObject;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.util.*;

import static main.RealPersonHelper.rpHash;

@RestController
public class AdminApi {
    private String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALnuZ0G2j0BslFeqTo6+4FqXFJOlXEFTOr7gSrXVcuw4xgA8vl17Dekuf6Xe1PASCcxgd3Vwwj4qAWRT4wli45KLH+2RQ0+5Hp/UDSwoN+Gt60mo/i3RNDcNbREDUXVIsZ/cEoRhTwqgAa542CfXFo2WW0/mkr45gog99hZfzO/3AgMBAAECgYEAqAhI0R6HwBVgywljDaZ7Y+N9+SFMis0JHk/dtQ8+WTXF25kKAbmgDMi1G9aSYpQqvsW/sCdDpKKZt24IlQcF2nPJDeX2hiR1yOddxdT3gCGU1FifOQbLtJSpDjnPVPv8xlbq2TBh0dMTwmw4rWErr7k9IukiFz4taZzJu9ZuiaECQQDhmugo76r+cstkX6/Pnquk+YK2OwU0HgUVEsFRIDWrOMLOnGr3TlqaS5w+aVEzVZlHAUWaM4TCfzgjW1w4xpCNAkEA0vsmgM5pvjurXp55PU98SxI4cfFmYQD78KbsmYgHHgJWJZvtaqWMDpu3Unf+p+7e0MqpMVyubLSu0k3bZhFrkwJAbjAjngLY6GgW8RJPIz+jI/N9pRWUiAz1fIJpiv8JV3RkqVb6Y4mQdPXi/RShRUpUj7qOTS9Gc1yBhbtUgqJw7QJAZewGUbtlwgy5YOBV2ZSIiwumUQU5XCtJipxZOGJKbejbDRgivZ5z5qq7CRAG6cGvylXdHoeTi9hNjDMohkpsGQJBALR+tMOXXHg4CNVCdHGGBI/9MP/RpAzeGhWgblCejYIkTJ39on3hIYtzhouykYb2pVpDmjHYVaXJWh4N9Aw31Pk=";

    /**
     * 該api在瀏覽器自動登陸的時候呼叫。因改换使用Cookie的原因，原来的流程正在改，这个api暂时没有用到，但勿删。
     * @param request: HttpServletRequest
     */
    @PostMapping("/api/checklogintoken")
    public ResponseEntity<?> checkLoginToken(HttpServletRequest request) {
        String token = null;
        Map<String, String[]> map = request.getParameterMap();
        if (map.containsKey("logtoken")) {
            token = map.get("logtoken")[0];
        }

        JSONObject result = new JSONObject();

        if (token == null) {
            result.put("result", 404);
            result.put("description", "参数错误");
        }
        else {
            String decryptedToken = TokenUtils.decrypt(token);

            JSONObject jo = new JSONObject(decryptedToken);
            String uuid = (String) jo.get("uuid");
            String timeStr = (String) jo.get("time");

            //TODO: 比较时间是否在一个月之内
            MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfUser);
            FindIterable<Document> findIterable = collection.find(new BasicDBObject("uuid", uuid));
            findIterable.limit(1);
            MongoCursor<Document> cursor = findIterable.iterator();
            if (cursor.hasNext()) {
                Document doc = cursor.next();
                result.put("user", doc.get("account"));
                token = TokenUtils.generateClientToken(uuid);
                result.put("token", token);
                result.put("result", 200);
                result.put("description", "ok");
            }
            else {
                Logman.error("Find a uuid of user not in db: " + uuid);

                result.put("result", 404);
                result.put("description", "user uuid not find in db.");
            }
            cursor.close();
        }

        return ResponseEntity.ok(result.toString());
    }

    @PostMapping("/api/userlogincheck")
    public ResponseEntity<?> userLoginCheck(HttpServletRequest request, @RequestParam("param") String param) {
        String account = null, encryptedKey = null;
        Map<String, String[]> map = request.getParameterMap();
        if (map.containsKey("account")) {
            account = map.get("account")[0];
        }
        if (map.containsKey("key")) {
            encryptedKey = map.get("key")[0];
        }

        String result;
        if (account == null || account.isEmpty() || encryptedKey == null || encryptedKey.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "请求登录的数据中没有身份证明").toString();
        }
        else {
            String secret = RSAUtils.decryptDataOnJava(encryptedKey, privateKey);
            String decryptedText = AESUtils.decrypt(account, secret);

            JSONObject jo = new JSONObject(decryptedText);
            String accnt = (String) jo.get("account");
            String password = (String) jo.get("password");

            MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfUser);
            FindIterable<Document> findIterable;
            if (param.equals("email")) {
                findIterable = collection.find(new BasicDBObject("email", accnt));
            }
            else {
                findIterable =collection.find(new BasicDBObject("account", accnt));
            }

            MongoCursor<Document> cursor = findIterable.iterator();
            if (cursor.hasNext()) {
                Document doc = cursor.next();

                String passwordHash = PasswordHelper.hashPassword(password);

                if (passwordHash != null) {
                    if (!passwordHash.equals(doc.get("password"))) {
                        //账户名对，但是密码不对，怀疑篡改
                        result = ResponseHelper.createBasicJsonObject(404, "用户名和密码不匹配").toString();
                    } else {
                        JSONObject jor = ResponseHelper.createBasicJsonObject(200, "ok");
                        String uuid = (String) doc.get("uuid");
                        jor.put("token", TokenUtils.generateClientToken(uuid));
                        result = jor.toString();
                    }
                }
                else {
                    result = ResponseHelper.createBasicJsonObject(404, "哈希密码发生错误").toString();
                }
            }
            else {
               result = ResponseHelper.createBasicJsonObject(404, "没有相应的用户名存在").toString();
            }
            cursor.close();
        }

        return ResponseEntity.ok(result);
    }

    /**
     * 該api在瀏覽器註冊頁面，用戶點擊註冊按鈕后被呼叫。用來檢測一個可能是新賬號是否合法等。
     * 提交的內容，是json格式：{"account": account, "key": key}。account是AES加密過的數據，
     * AES的密鑰是128bit的隨機數。該密鑰被客戶端用公鑰DES加密。所以，服務器得到這個加密的key
     * 之後，需要先用私鑰DES解密，然後再通過解密出的key進行AES解密account數據。
     * @param request: HttpServletRequest
     */
    @PostMapping("/api/register")
    public ResponseEntity<?> registerAccount(HttpServletRequest request) {
        String accountToEvaluate = null, encryptedKey = null, clientHash = null, captcha = null;
        Map<String, String[]> map = request.getParameterMap();
        if (map.containsKey("account")) {
            accountToEvaluate = map.get("account")[0];
        }
        if (map.containsKey("key")) {
            encryptedKey = map.get("key")[0];
        }
        if (map.containsKey("clientHash")) {
            clientHash = map.get("clientHash")[0];
        }
        if (map.containsKey("captcha")) {
            captcha = map.get("captcha")[0];
        }

        JSONObject resultJo = new JSONObject();

        if (clientHash == null || clientHash.isEmpty()) {
            resultJo.put("result", 404);
            resultJo.put("description", "没有提供图形验证码信息");
        }
        else {
            String rpHash = rpHash(captcha);

            if (rpHash.equals(clientHash)) {
                if (accountToEvaluate == null || encryptedKey == null || accountToEvaluate.isEmpty() || encryptedKey.isEmpty()) {
                    resultJo.put("result", 404);
                    resultJo.put("description", "参数错误，account、key和clientHash必须都提供");
                }
                else {
                    try {
                        String secret = RSAUtils.decryptDataOnJava(encryptedKey, privateKey);
                        String decryptedText = AESUtils.decrypt(accountToEvaluate, secret);

                        JSONObject jo = new JSONObject(decryptedText);
                        String account = (String) jo.get("account");
                        String password = (String) jo.get("password");

                        MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfUser);
                        long accountCount = collection.countDocuments(new BasicDBObject("account", account));

                        if (accountCount > 0) {
                            resultJo.put("result", 404);
                            resultJo.put("description", "用户已经存在");
                        }
                        else {
                            // save to db
                            String uuid = UUID.randomUUID().toString();

                            MessageDigest md = MessageDigest.getInstance("MD5");
                            md.update(password.getBytes());
                            byte[] digest = md.digest();
                            String passwordHash = DatatypeConverter.printHexBinary(digest);

                            Document document = new Document("account", account)
                                    .append("password", passwordHash)
                                    .append("phone", "")
                                    .append("wechat", "")
                                    .append("zhifubao", "")
                                    .append("email", "")
                                    .append("uuid", uuid)
                                    .append("money", 0)
                                    .append("role", "normal");
                            collection.insertOne(document);

                            String token = TokenUtils.generateClientToken(uuid);

                            resultJo.put("result", 200);
                            resultJo.put("description", "成功");
                            resultJo.put("token", token);
                        }
                    } catch (Exception e) {
                        Logman.fatal(e.getStackTrace().toString());
                    }
                }
            }
            else {
                resultJo.put("result", 1404);
                resultJo.put("description", "图形验证码不对，请重试");
            }
        }

        return ResponseEntity.ok(resultJo.toString());
    }

    /**
     * 該api在瀏覽器後臺登陸頁面，由登録按鈕通過ajax觸發。客戶端的賬戶和密碼都是通過AES配合DES加密過的，
     * 所以首先需要解密。解開后，需要查找用戶在數據庫中的存在性，還有用戶的role性質（普通用戶不能進admin）
     * @param request: HttpServletRequest
     */
    @PostMapping("/api/adminlogincheck")
    public ResponseEntity<?> adminLoginCheck(HttpServletRequest request) {
        String accountToEvaluate = null, encryptedKey = null;
        String[] infos = RequestHelper.getAccountInfosFromRequest(request);
        accountToEvaluate = infos[0];
        encryptedKey = infos[1];

        String result;
        if (accountToEvaluate == null || accountToEvaluate.isEmpty() || encryptedKey == null || encryptedKey.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "请求登录的数据中没有身份证明").toString();
        }
        else {
            String secret = RSAUtils.decryptDataOnJava(encryptedKey, privateKey);
            String decryptedText = AESUtils.decrypt(accountToEvaluate, secret);

            JSONObject jo = new JSONObject(decryptedText);
            String account = (String) jo.get("account");
            String password = (String) jo.get("password");

            MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfUser);
            FindIterable<Document> findIterable = collection.find(new BasicDBObject("account", account));
            MongoCursor<Document> cursor = findIterable.iterator();
            if (!cursor.hasNext()) {
                findIterable = collection.find(new BasicDBObject("email", account));
                cursor = findIterable.iterator();
            }

            if (cursor.hasNext()) {
                Document doc = cursor.next();

                String role = (String) doc.get("role");
                if (!role.equals("admin")) {
                    result = ResponseHelper.createBasicJsonObject(404, "该用户没有后台权限").toString();
                }
                else {
                    String passwordHash = PasswordHelper.hashPassword(password);

                    if (passwordHash != null) {
                        if (!passwordHash.equals(doc.get("password"))) {
                            //账户名对，但是密码不对，怀疑篡改
                            result = ResponseHelper.createBasicJsonObject(404, "用户名和密码不匹配").toString();
                        } else {
                            JSONObject jor = ResponseHelper.createBasicJsonObject(200, "ok");
                            String uuid = (String) doc.get("uuid");
                            jor.put("token", TokenUtils.generateClientToken(uuid));
                            result = jor.toString();
                        }
                    }
                    else {
                        result = ResponseHelper.createBasicJsonObject(404, "哈希密码发生错误").toString();
                    }
                }
            }
            else {
                result = ResponseHelper.createBasicJsonObject(404, "没有相应的用户名存在").toString();
            }
            cursor.close();
        }

        return ResponseEntity.ok(result);
    }

    /**
     * 邮箱验证点击后进入的画面，用户输入两次密码后正式注册时发生的api呼叫
     * @param request
     * @return
     * @throws Exception
     */
    @PostMapping("/api/registerbyemail")
    public ResponseEntity<?> registerByEmail(HttpServletRequest request) {
        String[] infos = RequestHelper.getAccountInfosFromRequest(request);
        String accountEncrypted = infos[0], keyEncrypted = infos[1];

        String result;
        if (accountEncrypted == null || accountEncrypted.isEmpty() || keyEncrypted == null || keyEncrypted.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "请求登录的数据中没有身份证明").toString();
        }
        else {
            String secret = RSAUtils.decryptDataOnJava(keyEncrypted, privateKey);
            String decryptedText = AESUtils.decrypt(accountEncrypted, secret);

            JSONObject jo = new JSONObject(decryptedText);
            String account = (String) jo.get("account");
            String password = (String) jo.get("password");

            MongoCollection<Document> collection = DatabaseMan.Instance().GetCollection(GlobalConfig.collectionNameOfUser);
            long count = collection.countDocuments(Filters.eq("email", account));
            if (count > 0) {
                result = ResponseHelper.createBasicJsonObject(404, "电子邮箱已经存在").toString();
            }
            else {
                String passwordHash = PasswordHelper.hashPassword(password);

                if (passwordHash != null) {
                    String uuid = UUID.randomUUID().toString();
                    String randomNickName = RandomNameHelper.fetchRandomNameNotUsed(true);
                    Document document = new Document("account", randomNickName)
                            .append("password", passwordHash)
                            .append("phone", "")
                            .append("wechat", "")
                            .append("zhifubao", "")
                            .append("email", account)
                            .append("uuid", uuid)
                            .append("money", 0)
                            .append("role", "normal");

                    collection.insertOne(document);

                    JSONObject resultJo = ResponseHelper.createBasicJsonObject(200, "ok");

                    resultJo.put("token", TokenUtils.generateClientToken(uuid));
                    result = resultJo.toString();
                }
                else {
                    result = ResponseHelper.createBasicJsonObject(404, "哈希密码发生错误").toString();
                }
            }
        }

        return ResponseEntity.ok(result);
    }

    /**
     * 注册的时候填写邮箱，点击完成后发起的api呼叫
     * @param request
     * @return
     */
    @PostMapping("/api/mailregister")
    public ResponseEntity<?> mailRegister(HttpServletRequest request) {
        String email = null, clientHash = null, captcha = null;
        Map<String, String[]> map = request.getParameterMap();
        if (map.containsKey("email")) {
            email = map.get("email")[0];
        }
        if (map.containsKey("clientHash")) {
            clientHash = map.get("clientHash")[0];
        }
        if (map.containsKey("captcha")) {
            captcha = map.get("captcha")[0];
        }

        String result = null;
        if (email == null || email.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "email没有提供").toString();
        }
        else if (clientHash == null || clientHash.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "clientHash没有提供").toString();
        }
        else if (captcha == null || captcha.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "没有提供captcha").toString();
        }
        else {
            if (AccountHelper.isExistFromEmail(email)) {
                result = ResponseHelper.createBasicJsonObject(404, "Email已经被使用").toString();
            }
            else {
                String rpHash = rpHash(captcha);

                if (rpHash.equals(clientHash)) {
                    result = ResponseHelper.createBasicJsonObject(200, "ok").toString();
                } else {
                    result = ResponseHelper.createBasicJsonObject(1404, "图形验证码不对").toString();
                }
            }
        }

        return ResponseEntity.ok(result);
    }

    /**
     * 用户点击邮箱中的重置密码链接跳转到的页面，输入两次密码后点击完成会呼叫的api
     * @param request
     * @return
     */
    @PostMapping("/api/updatepasswordbyemail")
    public ResponseEntity<?> updatePasswordByEmail(HttpServletRequest request) throws Exception {
        String[] infos = RequestHelper.getAccountInfosFromRequest(request);
        String accountEncrypted = infos[0], keyEncrypted = infos[1];

        String result;
        if (accountEncrypted == null || accountEncrypted.isEmpty() || keyEncrypted == null || keyEncrypted.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "请求中没有有效的数据").toString();
        }
        else {
            String secret = RSAUtils.decryptDataOnJava(keyEncrypted, privateKey);
            String decryptedText = AESUtils.decrypt(accountEncrypted, secret);
            JSONObject jo = new JSONObject(decryptedText);
            String email = (String) jo.get("email");
            String password = (String) jo.get("password");

            if (email == null || email.isEmpty()) {
                result = ResponseHelper.createBasicJsonObject(404, "邮箱地址不正确").toString();
            } else if (password == null || password.isEmpty()) {
                result = ResponseHelper.createBasicJsonObject(404, "密码没有提供或者是空").toString();
            } else {
                String passwordHash = PasswordHelper.hashPassword(password);
                if (passwordHash != null) {
                    AccountHelper.updatePasswordFromEmail(email, passwordHash);
                    result = ResponseHelper.createBasicJsonObject(200, "ok").toString();
                }
                else {
                    result = ResponseHelper.createBasicJsonObject(404, "哈希密码发生错误").toString();
                }
            }
        }

        return ResponseEntity.ok(result);
    }

    @PostMapping("/api/modifypassword")
    public ResponseEntity<?> modifyPassword(HttpServletRequest request) {
        String[] infos = RequestHelper.getAccountInfosFromRequest(request);
        String passwordInfo = infos[0], keyEncrypted = infos[1];

        String result;
        if (passwordInfo == null || passwordInfo.isEmpty() || keyEncrypted == null || keyEncrypted.isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "请求中没有有效数据").toString();
        }
        else {
            String secret = RSAUtils.decryptDataOnJava(keyEncrypted, privateKey);
            String decryptedText = AESUtils.decrypt(passwordInfo, secret);

            JSONObject jo = new JSONObject(decryptedText);
            String password = (String) jo.get("password");
            String newpassword = (String) jo.get("newpassword");
            String uuid = (String) jo.get("uuid");

            String oldPasswordHash = PasswordHelper.hashPassword(password);
            String newPasswordHash = PasswordHelper.hashPassword(newpassword);

            if (!AccountHelper.isExistFromUUID(uuid)) {
                result = ResponseHelper.createBasicJsonObject(404, "uuid对应的用户不存在").toString();
            }
            else {
                if (AccountHelper.isPasswordMatch(uuid, oldPasswordHash)) {
                    AccountHelper.updatePasswordFromUUID(uuid, newPasswordHash);
                    result = ResponseHelper.createBasicJsonObject(200, "ok").toString();
                }
                else {
                    result = ResponseHelper.createBasicJsonObject(404, "输入的旧密码不对").toString();
                }
            }
        }

        return ResponseEntity.ok(result);
    }

    @PostMapping("/api/modifynick")
    public ResponseEntity<?> modifyNick(HttpServletRequest request) {
        String result;
        String oldNick = null, newNick = null, uuid = null;
        Map<String, String[]> map = request.getParameterMap();
        if (map.containsKey("oldNick")) {
            oldNick = map.get("oldNick")[0];
        }
        if (map.containsKey("newNick")) {
            newNick = map.get("newNick")[0];
        }
        if (map.containsKey("uuid")) {
            uuid = map.get("uuid")[0];
        }

        if (oldNick == null || oldNick.trim().isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "没有提供旧的昵称").toString();
        }
        else if (newNick == null || newNick.trim().isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "没有提供新的昵称").toString();
        }
        else if (uuid == null || uuid.trim().isEmpty()) {
            result = ResponseHelper.createBasicJsonObject(404, "没有提供uuid").toString();
        }
        else {
            if (AccountHelper.isNickExist(newNick)) {
                result = ResponseHelper.createBasicJsonObject(404, "修改的新昵称已经被使用").toString();
            }
            else {
                AccountHelper.updateNick(uuid, oldNick, newNick);
                result = ResponseHelper.createBasicJsonObject(200, "ok").toString();
            }
        }

        return ResponseEntity.ok(result);
    }
}
